package cn.dansj.common.utils.spring;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;

import java.util.HashSet;
import java.util.Set;

@SuppressWarnings({"unchecked", "rawtypes"})
public abstract class ScanPackageClass {
    public static class Builder {
        private final Set<String> packages = new HashSet<>();
        private Class filterClass = null;
        private final Set<Class<?>> classes = new HashSet<>();

        public Builder addPackage(String packageName) {
            packages.add(packageName);
            return this;
        }

        public Builder addClass(Class<?> clazz) {
            classes.add(clazz);
            return this;
        }

        public Builder clearClasses() {
            classes.clear();
            return this;
        }

        public Builder setFilterClass(Class filterClass) {
            this.filterClass = filterClass;
            return this;
        }

        public Builder clearPackage() {
            packages.clear();
            return this;
        }

        public Builder clearFilterClass() {
            filterClass = null;
            return this;
        }

        public Set<String> build() {
            final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
            if (filterClass == null) {
                scanner.addIncludeFilter(new AssignableTypeFilter(Object.class));
            } else if (filterClass.isAnnotation()) {
                scanner.addIncludeFilter(new AnnotationTypeFilter(filterClass));
            } else {
                // 扫描所有类，而不仅仅是带有特定注解的类
                scanner.addIncludeFilter(new AssignableTypeFilter(filterClass));
            }

            Set<String> classesSet = new HashSet<>();
            for (String basePackage : packages) {
                basePackage = basePackage.replace(".", "/");
                final Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);

                for (BeanDefinition beanDefinition : candidateComponents) {
                    classesSet.add(beanDefinition.getBeanClassName());
                }
            }

            for (Class<?> cls : classes) {
                classesSet.add(cls.getName());
            }
            return classesSet;
        }
    }

    public static Builder builder() {
        return new Builder();
    }
}
